package database

import (
	"bytes"
	"context"
	"strings"
	"time"

	"gitee.com/chenhonghua/ginorigin/log"
)

var MetadataLoader metadataLoader = metadataLoader{}

type metadataLoader struct{}

// 根据模型创建数据库表
func (ml metadataLoader) CreateTables(models ...interface{}) {
	if !c.EnableAutoMigrate || connection == nil || len(models) == 0 {
		return
	}
	err := GetConnection().AutoMigrate(models...)
	if err != nil {
		log.Fatal(err)
	}
	log.Debugf("模型库表创建完成:%v\n", models)
}

// 加载sql语句（多语句）
func (ml metadataLoader) LoadSql(sql string) {
	sqls := strings.Split(sql, "\n")
	var buffer bytes.Buffer
	for _, s := range sqls {
		if strings.HasPrefix(s, "--") || strings.HasPrefix(s, "SET ") { // 跳过注释和环境变量设置
			continue
		}
		if strings.HasPrefix(s, "CREATE DATABASE ") || strings.HasPrefix(s, "ALTER DATABASE ") || strings.HasPrefix(s, "GRANT ") || strings.HasPrefix(s, `\connect `) { // 跳过库和权限管理
			continue
		}
		if strings.HasPrefix(s, "CREATE EXTENSION ") || strings.HasPrefix(s, "COMMENT ON EXTENSION ") { // 跳过扩展管理
			continue
		}
		buffer.WriteString(s)
	}
	sql = buffer.String()
	sqls = strings.Split(sql, ";")
	frameSqls := []string{} // 表框架语句
	dataSqls := []string{}  // 数据语句
	for _, s := range sqls {
		if strings.HasPrefix(s, "CREATE ") || strings.HasPrefix(s, "DROP ") || strings.HasPrefix(s, "ALTER ") || strings.HasPrefix(s, "COMMENT ON ") {
			if strings.Contains(s, "OWNER TO ") { // 跳过表权限管理
				continue
			}
			if strings.Contains(s, "security_policy") || strings.Contains(s, "security_role") || strings.Contains(s, "security_user_role") { // 放行security模块的库表
				continue
			}
			frameSqls = append(frameSqls, s)
			continue
		}
		dataSqls = append(dataSqls, s)
	}

	//
	st := time.Now().UnixMilli()
	if e := doLoadSql(frameSqls); e != nil { //批量执行表结构sql
		log.Fatal(e)
	} else if e := doLoadSql(dataSqls); e != nil { //批量执行数据sql
		log.Fatal(e)
	}
	log.Debugf("批量执行sql语句完成，耗时：%d毫秒", time.Now().UnixMilli()-st)
}

// 批量执行sql
func doLoadSql(sqls []string) error {
	// st := time.Now().UnixMilli()
	conn, _ := sqldb.Conn(context.Background())
	// defer conn.Close() // 不能手动关闭连接，会导致连接池关闭，导致后续的数据库操作无法进行
	tx, _ := conn.BeginTx(context.Background(), nil)
	for _, s := range sqls {
		if _, err := tx.Exec(s); err != nil {
			log.Errorf("批量执行sql语句[%s]发生错误:%v\n", s, err)
			tx.Rollback()
			return err
		}
	}
	err := tx.Commit()
	if err != nil {
		log.Error(err)
	}
	// log.Debug(fmt.Sprintf("批量执行sql语句完成，耗时：%d毫秒", time.Now().UnixMilli()-st))
	return err
}
